package io.github.wslxm.springbootplus2.manage.sys.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.github.wslxm.springbootplus2.client.sys.model.vo.USysConfigVO;
import io.github.wslxm.springbootplus2.constant.CacheKey;
import io.github.wslxm.springbootplus2.core.base.model.BasePage;
import io.github.wslxm.springbootplus2.core.base.service.impl.BaseServiceImpl;
import io.github.wslxm.springbootplus2.core.config.error.ErrorException;
import io.github.wslxm.springbootplus2.core.constant.NumberConst;
import io.github.wslxm.springbootplus2.core.enums.BaseDic;
import io.github.wslxm.springbootplus2.core.result.ResultType;
import io.github.wslxm.springbootplus2.core.utils.XjBeanUtil;
import io.github.wslxm.springbootplus2.core.utils.tree.XjTreeUtil;
import io.github.wslxm.springbootplus2.core.utils.validated.XjValidUtil;
import io.github.wslxm.springbootplus2.manage.sys.mapper.SysConfigMapper;
import io.github.wslxm.springbootplus2.manage.sys.model.dto.SysConfigDTO;
import io.github.wslxm.springbootplus2.manage.sys.model.entity.SysConfig;
import io.github.wslxm.springbootplus2.manage.sys.model.query.SysConfigQuery;
import io.github.wslxm.springbootplus2.manage.sys.model.vo.SysConfigVO;
import io.github.wslxm.springbootplus2.manage.sys.service.SysConfigService;
import io.github.wslxm.springbootplus2.starter.redis.lock.XjDistributedLock;
import io.github.wslxm.springbootplus2.starter.redis.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 系统全局数据信息配置表
 * <p>
 *  ::本代码由[兮家小二]提供的代码生成器生成,如有问题,请手动修改 ::作者CSDN:https://blog.csdn.net/qq_41463655 
 * </p>
 * @author wangsong
 * @email 1720696548@qq.com
 * @date 2020-08-31 18:31:44
 */
@Service
public class SysConfigServiceImpl extends BaseServiceImpl<SysConfigMapper, SysConfig> implements SysConfigService {

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public List<SysConfigVO> tree() {
        LambdaQueryWrapper<SysConfig> queryWrapper = new LambdaQueryWrapper<SysConfig>()
                .select(SysConfig::getId, SysConfig::getPid, SysConfig::getName, SysConfig::getCode)
                .orderByAsc(SysConfig::getSort)
                .orderByDesc(SysConfig::getCreateTime);
        List<SysConfig> list = this.list(queryWrapper);
        List<SysConfigVO> configVos = XjBeanUtil.listVo(list, SysConfigVO.class);
        SysConfigVO configVO = XjTreeUtil.nextTree(configVos, NumberConst.ZERO + "");
        return configVO.getChildren();
    }

    @Override
    public BasePage<SysConfigVO> findPage(SysConfigQuery query) {
        LambdaQueryWrapper<SysConfig> queryWrapper = new LambdaQueryWrapper<SysConfig>()
                .orderByAsc(SysConfig::getSort)
                .orderByDesc(SysConfig::getCreateTime)
                .eq(StringUtils.isNotBlank(query.getPid()), SysConfig::getPid, query.getPid())
                .eq(StringUtils.isNotBlank(query.getCode()), SysConfig::getCode, query.getCode())
                .eq(query.getType() != null, SysConfig::getType, query.getType())
                .likeRight(StringUtils.isNotBlank(query.getName()), SysConfig::getName, query.getName());
        BasePage<SysConfigVO> resPage = null;
        resPage = XjBeanUtil.pageVo(this.page(new Page<>(query.getCurrent(), query.getSize()), queryWrapper), SysConfigVO.class);
        // 如果是富文本不返回内容
        for (SysConfigVO xjAdminConfigVO : resPage.getRecords()) {
            if (xjAdminConfigVO.getType().equals(BaseDic.ConfigType.V3.getValue())) {
                xjAdminConfigVO.setContent("详情查看");
            }
        }
        return resPage;
    }

    @Override
    public SysConfigVO findId(String id) {
        return XjBeanUtil.convert(this.getById(id), SysConfigVO.class);
    }

    @Override
    @XjDistributedLock(lockName = "'xj-sys-config_'+#dto.code", waitTime = 5L)
    public String insert(SysConfigDTO dto) {
        // 判code重复
        if (this.count(new LambdaQueryWrapper<SysConfig>().eq(SysConfig::getCode, dto.getCode())) > 0) {
            throw new ErrorException(ResultType.BASE_CODE_REPEAT);
        }
        SysConfig entity = dto.convert(SysConfig.class);
        boolean b = this.save(entity);
        return entity.getId();
    }


    @Override
    @Caching(evict = {
            @CacheEvict(cacheResolver = CacheKey.XJ_RESOLVER, value = CacheKey.CONFIG_BY_CODE, key = "#dto.code"),
            @CacheEvict(cacheResolver = CacheKey.XJ_RESOLVER, value = CacheKey.CONFIG_BY_CODES, allEntries = true),
            @CacheEvict(cacheResolver = CacheKey.XJ_RESOLVER, value = CacheKey.CONFIG_BY_P_CODE, allEntries = true)
    })
    @XjDistributedLock(lockName = "'xj-sys-config_'+#dto.code", waitTime = 5L)
    public boolean upd(String id, SysConfigDTO dto) {
        SysConfig config = this.getById(id);
        if (!config.getCode().equals(dto.getCode())) {
            if (this.count(new LambdaQueryWrapper<SysConfig>().eq(SysConfig::getCode, dto.getCode())) > 0) {
                throw new ErrorException(ResultType.BASE_CODE_REPEAT);
            }
        }
        SysConfig entity = dto.convert(SysConfig.class);
        entity.setId(id);
        boolean b = this.updateById(entity);
        this.delCache(config.getCode());
        return b;
    }

    @Override
    @Caching(evict = {
            @CacheEvict(cacheResolver = CacheKey.XJ_RESOLVER, value = CacheKey.CONFIG_BY_CODE, allEntries = true),
            @CacheEvict(cacheResolver = CacheKey.XJ_RESOLVER, value = CacheKey.CONFIG_BY_CODES, allEntries = true),
            @CacheEvict(cacheResolver = CacheKey.XJ_RESOLVER, value = CacheKey.CONFIG_BY_P_CODE, allEntries = true)
    })
    public boolean del(String id) {
        // 判断是否存在子级
        long count = this.count(new LambdaQueryWrapper<SysConfig>().eq(SysConfig::getPid, id));
        XjValidUtil.isTrue(count > 0, "sys.config.delete.Child");
        SysConfig config = this.getById(id);
        boolean b = this.removeById(id);
        this.delCache(config.getCode());
        return b;

    }

    @Override
    @Cacheable(cacheResolver = CacheKey.XJ_RESOLVER, value = CacheKey.CONFIG_BY_CODE, key = "#code", unless = "#result == null")
    public USysConfigVO findByCode(String code) {
        SysConfig xjAdminConfig = this.getOne(new LambdaQueryWrapper<SysConfig>().eq(SysConfig::getCode, code));
        return XjBeanUtil.convert(xjAdminConfig, USysConfigVO.class);
    }

    @Override
    @Cacheable(cacheResolver = CacheKey.XJ_RESOLVER, value = CacheKey.CONFIG_BY_CODES, key = "#codes", unless = "#result == null")
    public Map<String, USysConfigVO> findByCodes(String codes) {
        ArrayList<String> codeList = CollUtil.toList(codes.split(","));
        List<SysConfig> list = this.list(new LambdaQueryWrapper<SysConfig>().in(SysConfig::getCode, codeList));
        List<USysConfigVO> configVOS = XjBeanUtil.listVo(list, USysConfigVO.class);
        return configVOS.stream().collect(Collectors.toMap(USysConfigVO::getCode, p -> p));
    }


    @Override
    @Cacheable(cacheResolver = CacheKey.XJ_RESOLVER, value = CacheKey.CONFIG_BY_P_CODE, key = "#parentCode", unless = "#result == null")
    public Map<String, USysConfigVO> findByParentCode(String parentCode) {
        List<SysConfig> list = this.list(new LambdaQueryWrapper<SysConfig>().select(SysConfig::getId, SysConfig::getPid, SysConfig::getCode, SysConfig::getName));
        String pid = null;
        for (SysConfig config : list) {
            if (parentCode.equals(config.getCode())) {
                pid = config.getId();
                break;
            }
        }
        XjValidUtil.isTrue(pid == null, ResultType.BASE_NO_REQ_PARAM.getMsg());
        List<String> nextIds = XjTreeUtil.getNextIds(XjBeanUtil.listVo(list, SysConfigVO.class), pid);
        // 查询数据
        List<SysConfig> resList = this.list(new LambdaQueryWrapper<SysConfig>().in(SysConfig::getId, nextIds));
        List<USysConfigVO> configVOS = XjBeanUtil.listVo(resList, USysConfigVO.class);
        return configVOS.stream().collect(Collectors.toMap(USysConfigVO::getCode, p -> p));
    }


    /**
     *  在指定前缀情况时通过后缀删除缓存, 只限于 code 查询方法
     *  主要处理部分系统层级代码获取配置时无法实时更新问题
     */
    private void delCache(String code) {
        Set<String> keys = redisUtil.getKeysBySuffix(CacheKey.CONFIG_BY_CODE + "::" + code);
        redisUtil.delete(keys);
    }

}
